const Config = require("./config.js");
const uniID = require('uni-id');

const db = uniCloud.database();
const dbCmd = db.command;

function BaseCloud( { event, ctx , fnName  } ) {
	this.log( "请求参数：" , event);
	this.event = event ;
	this.ctx = ctx;
	this.params = event.params ;
	this.action = event.action ;
	this.fnName = fnName ;
	this.fullPath = `${fnName}/${this.action}`;
	this.token = event.uniIdToken;
	this.uniID = uniID ;
}


function isHit(handles , action){
	for (var i = 0; i < handles.length; i++) {
		var item = handles[i] ;
		if ( (Object.prototype.toString.call(item) === '[object RegExp]' && item.test(action)) || item == action  ) {
			return true ;
		}
	}
	return false ;
}

function isHandle( inter , action){
	var handles = inter.handle || [];
	if (handles.length > 0) {
		return isHit(handles , action) ;
	}
	var clears = inter.clear || [] ;
	return !isHit(clears , action) ;
}

async function globalInters() {
	if (typeof Config.inters != 'object') return;
	this.log("Interceptors:");
	for (var interName in Config.inters) {
		var inter = Config.inters[interName] ;
		if (this.isObject(inter)) {
			var isHit = isHandle( inter , this.fullPath );
			if (!isHit) {
				this.log("拦截器未命中：", interName , " ===> " , this.fullPath );
				continue ;
			}
			inter = inter.invoke ;
		}
		this.log(`  ${interName}()`);
		this.BREAK = true ;
		this.invokeInter = inter ;
		var interRes = await this.invokeInter(this.ATTRS);
		if ( this.BREAK ) {
			console.log("请求被拦截：" , interName , " ===> " , this.fullPath );
			return interRes ;
		}
	}
}

function getPathByAction(action){
	var lastSplit = action ? action.lastIndexOf("/") : -1 ;
	var methodName = action ? action.substr( lastSplit + 1 ) : '' ;
	var path = lastSplit == -1 ? methodName : action.substr(0 , action.lastIndexOf("/"));
	return { path , methodName , isDefault : lastSplit == -1 } ;
}

function setConditions(where , keyArr , conditionKey , _this){
	Array.isArray(keyArr) && keyArr.forEach(keys =>{
		if (!keys) {
			return ;
		}
		var arr = keys.split(",") ;
		var key = arr[0] ;
		var val = _this.params[key] ;
		if (!_this.isNull(val)) {
			var queryKey = arr.length > 1 ? arr[1] : arr[0] ;
			where[queryKey] = dbCmd[conditionKey](val);
		}
	});
}

BaseCloud.prototype = {
	
	globalInters  ,
	
	invoke: async function(absolutePath) {
		try{
			var { methodName , path , isDefault  } = getPathByAction(this.action) ;
			var controller = null ;
			controller = require(`${absolutePath}/${path}`);
		}catch(e){
			console.error(e);
			return {
				state : 404 ,
				msg: `action is undefined : ${this.action}`
			};
		}
		var fn = isDefault ? controller : controller[methodName] ;
		if (typeof fn != 'function') {
			return {
				state : 404 ,
				msg: 'action is undefined'
			};
		}
		//全局拦截器
		var interRes = await this.globalInters();
		if ( this.BREAK ) {
			return interRes ;
		}
		this.invokeAction = fn ;
		return await this.invokeAction( this.ATTRS || {});
	},
	
	next : function(e){
		this.BREAK = false ;
	},
	
	setAttr : function(obj){
		if (!this.isObject(obj)) {
			return ;
		}
		this.ATTRS = Object.assign(this.ATTRS || {} , obj );
	},
	
	getAttr : function(key){
		return this.ATTRS[key] ;
	},

	setMaxOrderNum: async function(data, collection, where) {
		if (!this.isNull(data.orderNum)) {
			return;
		}
		if (!this.isEmptyObject(where)) {
			collection = collection.where(where);
		}
		var dataInDB = this.findFirst(await collection.orderBy("orderNum", "desc").limit(1).get());
		data.orderNum = null == dataInDB ? 1 : parseInt(dataInDB.orderNum) + 1;
	},
	
	findFirst : function(dataInDB) {
		return dataInDB && dataInDB.data && dataInDB.data.length > 0 ? dataInDB.data[0] : null;
	},
	
	find : function(dataInDB) {
		return dataInDB && dataInDB.data && dataInDB.data.length > 0 ? dataInDB.data : [];
	},

	updateById: async function(collection, data) {
		var id = data._id;
		if (!id) throw "_id参数缺失";
		delete data._id;
		console.log("data: " , data);
		console.log("id: " + id);
		var res = await collection.doc(id).update(data);
		return res.updated;
	},
	
	paginate: async function({ collection , where = {} , field = {} , orderBy , eq , like , gte , lte , gt , lt , pageNumber = 1, pageSize = 10 }){
		setConditions(where , eq , "eq" , this);
		setConditions(where , gte , "gte" , this);
		setConditions(where , gt , "gt" , this);
		setConditions(where , lte , "lte" , this);
		setConditions(where , lt , "lt" , this);
		
		if (this.isArray(like)) {
			for (var i = 0; i < like.length; i++) {
				var key = like[i] ;
				var val = this.params[key] ;
				if (!this.isNull(val)) {
					where[key] = new RegExp( val );
				}
			}
		}
		if (this.isEmptyObject(where)) {
			where._id = dbCmd.exists(true); //兼容阿里云的count()查询bug
		}
		collection = collection.where(where);
		var countRes = await collection.count();
		if (!this.isEmptyObject(field)) {
			collection = collection.field(field) ;
		}
		if (this.isString(orderBy)) {
			var arr = orderBy.trim().split(",");
			for (var i = 0; i < arr.length; i++) {
				var curOrder = arr[i] ;
				var isDesc = curOrder.indexOf("desc") > -1 ;
				curOrder = curOrder.replace("desc","").replace("asc" , "").trim();
				collection = collection.orderBy(curOrder , isDesc ? 'desc' : 'asc');
			}
		}
		var list = this.find( await collection.skip( (pageNumber-1) * pageSize ).limit(pageSize).get() ) ;
		var totalRow = countRes.total ;
		var totalPage = Math.ceil(totalRow/pageSize) ;
		return {
			list ,
			pageNumber ,
			pageSize ,
			totalRow ,
			totalPage ,
			lastPage : pageNumber == totalPage ,
			firstPage : pageNumber == 1 
		};
	},
	
	/**
	 * @param {Object} prefix
	 * {
		 "x.name" : "王小二" ,
		 "x.school.name" : "北京大学",
		 sex : "女"
	  }
	  过滤后：
	  {
		  name : "王小二" ,
		  school : {
			  name : "北京大学"
		  }
	  }
	 */
	getModel: function(prefix = "x" , keepKeys ) {
		var data = {};
		for (var key in this.params) {
			if ( !!prefix && key.indexOf(prefix + ".") == -1) {
				continue ;
			}
			var value = this.params[key] ;
			if (!!prefix) {
				key = key.replace(prefix + ".", '') ;
			}
			var keyArr = key.split('.');
			var json = {} ;
			for (var i = keyArr.length - 1 ; i > -1 ; i-- ) {
				var curKey = keyArr[i] ;
				if (i == 0) {
					data[curKey] = value ;
					break ;
				}
				json[curKey] = value ;
				value = JSON.parse(JSON.stringify(json)) ;
			}
		}
		return this.keep( data , keepKeys) ;
	},
	
	keep : function( data , keys){
		if (!keys || !this.isObject(data) ) {
			return data ;
		}
		var keyArr = keys.split(",");
		for (let key in data) {
			if (keyArr.indexOf(key) == -1) {
				delete data[key] ;
			}
		}
		return data ;
	},
	
	log:function(){
		if (Config.isDebug) {
		   console.log(...arguments);
		}
	},

	isNull: function(obj) {
		return obj !== 0 && obj !== false && !obj ;
	},
	
	isObject : function (obj) {
	  return Object.prototype.toString.call(obj) === '[object Object]'
	},
	
	isEmptyObject:function(obj){
		return this.isObject(obj) && Object.keys(obj).length == 0 ;
	},
	
	isFn : function(fn){
		return typeof fn == "function" ;
	},
	
	isNumber:function(num){
		return num !== false && num !== true && !isNaN(Number(num)) ;
	},
	
	isArray:function(arr){
		return Array.isArray(arr);
	},
	
	isString : function(obj){
		return  Object.prototype.toString.call(obj) === '[object String]' ;
	},
	
	isDate:function(obj){
		return Object.prototype.toString.call(obj) === '[object Date]' ;
	},
	
	isReg:function(obj){
		return Object.prototype.toString.call(obj) === '[object RegExp]' ;
	},
	
	isRepeat:function( dataInDB , _id ){
		return null != dataInDB &&  ( this.isNull(_id)  || ( _id != dataInDB._id )) ;
	},

	ok: function(msg) {
		return {
			state: 'ok',
			msg: msg || '操作成功'
		};
	},

	fail: function(msg, state) {
		return {
			state: !state ? 'fail' : state,
			msg: msg || "系统异常，请稍后再试" ,
		};
	},
	
	DateKit: {
		
		addMinutes:function(minutes , date){
			date = date || this.now() ;
			return date + minutes * 60 * 1000 ;
		},
		
		addHours:function(hours , date){
			date = date || this.now() ;
			return date + hours * 60 * 60 * 1000 ;
		},
		
		addDays:function(days=0 , date){
			date = date || this.now() ;
			return date + days * 24 * 60 * 60 * 1000 ;
		},
		
		addMonths:function(months=0 , date){
			date = new Date(date || this.now()) ;
			date.setMonth(date.getMonth() + months );
			return date.getTime() ;
		},
		
		now:function(timestamp){
			timestamp = timestamp || Date.now();
			return timestamp + 8 * 60 * 60 * 1000 ;
		},
		
		formatMinNum :function (num){
			return num > 9 ? num : "0" + num ;
		},
		
		/**
		 * @param timeStr 日期类型的字符串
		 */
		parse:function(timeStr){
			var timestamp = Date.parse(timeStr);
			if (isNaN(timestamp)) {
				return null ;
			}
			return timestamp ;
		},
		
		toStr:function( timestamp , fileds ){
			if(!timestamp){
				return '' ;
			}
			var now = new Date(parseFloat(timestamp));
			var year = now.getFullYear();
			var month = this.formatMinNum(now.getMonth() + 1);
			var date = this.formatMinNum(now.getDate());
			var hour = this.formatMinNum(now.getHours());
			var minute = this.formatMinNum(now.getMinutes());
			var second = this.formatMinNum(now.getSeconds());
			if (fileds == 'seconds') {
				return `${year}-${month}-${date} ${hour}:${minute}:${second}`;
			}
			if (fileds == 'minute') {
				return `${year}-${month}-${date} ${hour}:${minute}`;
			}
			if (fileds == 'hour') {
				return `${year}-${month}-${date} ${hour}:00`;
			}
			if (fileds == 'day') {
				return `${year}-${month}-${date}`;
			}
			if (fileds == 'month') {
				return `${year}-${month}`;
			}
			if (fileds == 'year') {
				return `${year}年`;
			}
			return `${year}-${month}-${date} ${hour}:${minute}`;
		},
		
		/**
		 * @param time 毫秒数
		 * @return 入参时间距离当前时间的时间
		 */
		friendlyDate: function(time) {
			let ms = time - this.now() ;
			let num ;
			let quantifier ;
			let suffix = '后'
			if (ms < 0) {
				suffix = '前'
				ms = -ms ;
			}
			const seconds = Math.floor((ms) / 1000);
			const minutes = Math.floor(seconds / 60);
			const hours = Math.floor(minutes / 60);
			const days = Math.floor(hours / 24);
			const months = Math.floor(days / 30);
			const years = Math.floor(months / 12);
			switch (true) {
				case years > 0:
					num = years;
					quantifier = '年';
					break;
				case months > 0:
					num = months;
					quantifier = '月';
					break;
				case days > 0:
					num = days;
					quantifier = '天';
					break;
				case hours > 0:
					num = hours;
					quantifier = '小时';
					break;
				case minutes > 0:
					num = minutes;
					quantifier = '分钟';
					break;
				default:
					num = seconds;
					quantifier = '秒';
					break;
			}
			return `${num}${quantifier}${suffix}`;
		},
	}
};

module.exports = BaseCloud ;
